package pl.minim.unidefCMS {
	import pl.minim.unidefCMS.objects.AbstractUnidefObject;
	import pl.minim.unidefCMS.objects.UnidefClass;

	import com.googlecode.milib.base.MLBroadcastClass;
	import com.googlecode.milib.util.MLArrayUtil;

	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;

	/**
	 * @author Marek Brun
	 */
	public class UnidefManager extends MLBroadcastClass {

		public const event_RecivedData:String = 'RecivedData';

		private var classes:Dictionary;//of XML
		private var xml:XML;
		public var rootClass:UnidefClass;
		private var dictPredefinedsID_dict_PredefinedID:Dictionary;
		private var dictPredefinedsID_Array:Dictionary;
		private var dictXMLDef_PredefinedID:Dictionary;
		private var xmlORG:XML;
		private var xmlLoader:URLLoader;

		public function UnidefManager():void {
		}

		public function setData(xml:XML):void {
			//			try{
			xmlORG = xml.copy();
			classes = new Dictionary();
			dictPredefinedsID_dict_PredefinedID = new Dictionary();
			dictPredefinedsID_Array = new Dictionary();
			dictXMLDef_PredefinedID = new Dictionary();
			this.xml = xml;
				
			var i:uint,j:uint;
			var loop:XML;
			for(i = 0;i < xml.definition.classes.item.length();i++) {
				loop = xml.definition.classes.item[i];
				loop.@['className'] = loop.@id.toString();
				classes[loop.@id.toString()] = loop;
			}
				
			var predefinedsList:XMLList = xml.data.predefined[0].i;
			for(i = 0;i < predefinedsList.length();i++) {
				loop = predefinedsList[i];
				if(!classes[loop.@id.toString()]) {
					throw new Error('Can\'t find class "' + loop.@id.toString() + '"');
				}
				dictPredefinedsID_dict_PredefinedID[loop.@id.toString()] = new Dictionary();
				dictPredefinedsID_Array[loop.@id.toString()] = [];
				for(j = 0;j < loop.i.length();j++) {
					addPredefinedObj(loop.@id.toString(), loop.i[j].@id.toString(), loop.i[j]);
				}
			}
				
			if(XML(xml.data.root[0]).i.length()) {
				rootClass = UnidefClass(AbstractUnidefObject.createByXML(getXMLClassByID(xml.definition.rootClass[0].toString()), xml.data.root[0], this));
			} else {
				rootClass = createXMLDefClassByDefData(xml.definition.rootClass[0].toString());
				rootClass.id = xml.data.root[0].@id.toString();
					//Alert.show('Default data for root class '+xml.definition.rootClass[0].toString()+' is created.');
			}
				
			broadcastEvent(event_RecivedData);
//			}catch(e:Error){
//				dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, '['+e.errorID+']'+e.name+'\n'+e.message));
//				throw e;
//			}
		}

		public function addPredefinedObj(clazz:String, id:String, dataDefXML:XML):AbstractUnidefObject {
			var unidef:AbstractUnidefObject = AbstractUnidefObject.createByXML(classes[clazz], dataDefXML, this);
			unidef.id = id;
			dictXMLDef_PredefinedID[unidef] = id;
			dictPredefinedsID_dict_PredefinedID[clazz][id] = unidef;
			if(!dictPredefinedsID_Array[clazz]) {
				dictPredefinedsID_Array[clazz] = [];
			}
			dictPredefinedsID_Array[clazz].push(unidef);
			return unidef;
		}

		public function addPredefinedObjWithNoData(clazz:String, id:String):AbstractUnidefObject {
			return addPredefinedObj(clazz, id, null);
		}

		public function removePredefinedObj(clazz:String, unidef:UnidefClass):void {
			MLArrayUtil.remove(dictPredefinedsID_Array[clazz], unidef);
			delete dictXMLDef_PredefinedID[unidef];
			delete dictPredefinedsID_dict_PredefinedID[clazz][unidef.id];
			unidef.dispose();
			rootClass.reactToPredefinedObjectDelete(unidef);
		}

		public function createXMLDefClassByDefData(clazz:String):UnidefClass {
			return UnidefClass(AbstractUnidefObject.createByXML(getXMLClassByID(clazz), null, this));
		}

		public function getPredefined(clazz:String, id:String):AbstractUnidefObject {
			if(!dictPredefinedsID_dict_PredefinedID[clazz][id]) {
				throw new Error('Can\'t find predefined Unidef Object for class:' + clazz + ' and id:' + id);
			}
			return dictPredefinedsID_dict_PredefinedID[clazz][id];
		}

		public function getGotPredefined(clazz:String, id:String):Boolean {
			return Boolean(dictPredefinedsID_dict_PredefinedID[clazz][id]);
		}

		public function getPredefinedList(clazz:String):Array {
			return (dictPredefinedsID_Array[clazz] as Array).concat();
		}

		public function getPredefinedIdByXMLDef(unidef:AbstractUnidefObject):String {
			return dictXMLDef_PredefinedID[unidef];
		}

		public function getXMLClassByID(id:String):XML {
			if(!classes[id]) {
				throw new Error('Can\'t find XMLClass for  id:' + id);
			}
			return classes[id];
		}

		public function exportToXML():XML {
			var newXML:XML = xmlORG.copy();
			var dataXML:XML = rootClass.toXML();
			dataXML.setName('root');
			newXML.data.root = dataXML;
			var predefinedsList:XMLList = newXML.data.predefined[0].i;
			var predefinedObjects:XML;
			var list:Array;
			var i:uint,j:uint;
			var loop:XML;
			for(i = 0;i < predefinedsList.length();i++) {
				loop = predefinedsList[i];
				predefinedObjects = <i/>;
				predefinedObjects.@id = loop.@id.toString();
				list = getPredefinedList(loop.@id.toString());
				for(j = 0;j < list.length;j++) {
					predefinedObjects.appendChild(UnidefClass(list[j]).toXML());
				}
				newXML.data.predefined[0].i[i] = predefinedObjects;
			}
			
			return newXML;
		}

		public function export():ByteArray {
			var newXML:XML = exportToXML();
			var xmlInBytes:ByteArray = new ByteArray();
			XML.prettyPrinting = false;
			xmlInBytes.writeUTFBytes(newXML.toXMLString());
			xmlInBytes.compress();
			
			return xmlInBytes;
		}

		public function loadDataFromXML(fileURL:String):void {
			xmlLoader = new URLLoader();
			xmlLoader.addEventListener(Event.COMPLETE, onXMLLoaderComplete);
			xmlLoader.load(new URLRequest(fileURL));
		}

		public function loadDataFromXMLByteArray(fileURL:String):void {
			xmlLoader = new URLLoader();
			xmlLoader.addEventListener(Event.COMPLETE, onXMLBALoaderComplete);
			xmlLoader.dataFormat = URLLoaderDataFormat.BINARY;
			xmlLoader.load(new URLRequest("unidefxmlba"));
		}

		public function setDataByByteArray(xmlba:ByteArray):void {
			try {
				xmlba.uncompress();
			}catch(e:Error) {
				dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, 'Fail to uncompress data.\nBinary file should be send by FTP in binary transfer type.'));
			}
			xmlba.position = 0;
			var xmlStr:String = xmlba.readUTFBytes(xmlba.length);
//			xmlStr = xmlStr.replace(/\r\n/g, '\n');
//			xmlStr = xmlStr.replace(/\n\r/g, '\n');
			setData(new XML(xmlStr));
		}

		//********************************************************************************************
		//		events for UnidefManager
		//********************************************************************************************
		protected function onXMLLoaderComplete(event:Event):void {
			setData(new XML(xmlLoader.data));
		}

		protected function onXMLBALoaderComplete(event:Event):void {
			setDataByByteArray(xmlLoader.data);
		}

		public function exportToRawData():Object {
			return rootClass.exportToRawData();
		}
	}
}
